package top.milkbox.sys.modular.menu.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.milkbox.common.exceprion.CommonServiceException;
import top.milkbox.common.service.impl.CommonServiceImpl;
import top.milkbox.common.utils.CommonUtil;
import top.milkbox.sys.modular.menu.entity.SysMenuEntity;
import top.milkbox.sys.modular.menu.mapStruct.SysMenuMapStruct;
import top.milkbox.sys.modular.menu.mapper.SysMenuMapper;
import top.milkbox.sys.modular.menu.param.SysMenuAddParam;
import top.milkbox.sys.modular.menu.param.SysMenuEditParam;
import top.milkbox.sys.modular.menu.param.SysMenuIdParam;
import top.milkbox.sys.modular.menu.service.SysMenuService;
import top.milkbox.sys.modular.menu.vo.SysMenuVo;
import top.milkbox.sys.modular.relationship.entity.SysRelationshipEntity;
import top.milkbox.sys.modular.relationship.enums.SysRelationshipTypeEnum;
import top.milkbox.sys.modular.relationship.service.SysRelationshipService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 系统_菜单表（sys_menu）服务层实现类
 *
 * @author milkbox
 * @date 2024-1-29
 */
@Slf4j
@Service
public class SysMenuServiceImpl extends CommonServiceImpl<SysMenuMapper, SysMenuEntity> implements SysMenuService {

    @Autowired
    private SysMenuMapStruct sysMenuMapStruct;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Lazy
    @Autowired
    private SysRelationshipService sysRelationshipService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysMenuAddParam addParam) {
        SysMenuEntity entity = sysMenuMapStruct.addParamToEntity(addParam);
        // 如果未指定父级id，则默认为0
        if (ObjectUtil.isEmpty(entity.getParentId())) {
            entity.setParentId(0);
        } else if (entity.getParentId() != 0 && ObjectUtil.isEmpty(super.getById(entity.getParentId()))) {
            // 验证上级id是否存在，若不存在，则抛出异常
            throw new CommonServiceException("上级id（{}）不存在", entity.getParentId());
        }
        // 若未指定可见性，则默认为false不可见
        if (ObjectUtil.isEmpty(entity.getIsShow())) {
            entity.setIsShow(false);
        }
        super.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<SysMenuIdParam> paramList) {
        super.removeByIds(paramList.stream().map(SysMenuIdParam::getId).toList());
        // TODO 此处还需要删除关系表的菜单关联信息
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replaceDelete(SysMenuIdParam idParam) {
        SysMenuEntity entity = findEntity(idParam.getId());
        // 从数据库中查询当前节点的下级节点
        List<SysMenuEntity> childrenList =
                super.list(new LambdaUpdateWrapper<SysMenuEntity>().eq(SysMenuEntity::getParentId, idParam.getId()));
        // 如果下级节点不为空，则批量更新下级节点的父级id
        if (ObjectUtil.isNotEmpty(childrenList)) {
            // 提取下级节点的id
            List<Integer> childrenIdList = childrenList.stream().map(SysMenuEntity::getId).toList();
            // 批量更新下级节点的父级id，下级节点的父级id等于当前节点的父级id
            LambdaUpdateWrapper<SysMenuEntity> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(SysMenuEntity::getParentId, entity.getParentId())
                    .in(SysMenuEntity::getId, childrenIdList);
            super.update(updateWrapper);
        }
        // 删除当前节点
        super.removeById(idParam.getId());
        // TODO 此处还需要删除关系表的菜单关联信息
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cascadingDelete(SysMenuIdParam idParam) {
        // 检查当前节点是否存在
        findEntity(idParam.getId());
        // 存储待删除的节点id
        ArrayList<Integer> deleteIdList = new ArrayList<>();
        // 存储每一层节点的父id，第一次为当前节点的id
        ArrayList<Integer> parentIdList = new ArrayList<>(Collections.singletonList(idParam.getId()));
        // 循环查询parentIdList的下级节点（广度优先）
        while (true) {
            // 查询parentIdList的下级节点
            List<SysMenuEntity> menuList =
                    super.list(new LambdaQueryWrapper<SysMenuEntity>().in(SysMenuEntity::getParentId, parentIdList));
            // 如果下级节点不为空，则将下级节点的id加入到待删除集合中，并作为下一层的父级id
            if (ObjectUtil.isNotEmpty(menuList)) {
                // 提取下级节点的id
                List<Integer> menuIdList = menuList.stream().map(SysMenuEntity::getId).toList();
                // 将当前一层的节点id加入到待删除集合中
                deleteIdList.addAll(menuIdList);
                // 将当前一层的节点id作为下一层的父级id
                parentIdList = new ArrayList<>(menuIdList);
            } else {
                // 如果下级节点为空，则跳出循环
                break;
            }
        }
        // 删除所有待删除的节点
        super.removeByIds(deleteIdList);
        // TODO 此处还需要删除关系表的菜单关联信息
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(SysMenuEditParam editParam) {
        SysMenuEntity oldEntity = findEntity(editParam.getId());
        SysMenuEntity entity = sysMenuMapStruct.editParamToEntity(editParam);
        // 如果未指定父级id，则默认为0
        if (ObjectUtil.isEmpty(entity.getParentId())) {
            entity.setParentId(0);
        } else if (entity.getParentId() != 0 &&
                !entity.getParentId().equals(oldEntity.getParentId()) &&
                ObjectUtil.isEmpty(super.getById(entity.getParentId()))) {
            // 验证上级id是否存在，若不存在，则抛出异常
            throw new CommonServiceException("上级id（{}）不存在", entity.getParentId());
        }
        // 若未指定可见性，则默认为false不可见
        if (ObjectUtil.isEmpty(entity.getIsShow())) {
            entity.setIsShow(false);
        }
        super.updateById(entity);
    }

    @Override
    public SysMenuVo detail(SysMenuIdParam idParam) {
        SysMenuEntity entity = findEntity(idParam.getId());
        SysMenuVo vo = sysMenuMapStruct.entityToVo(entity);
        // 此处进行数据翻译操作，，根据不同的业务逻辑将entity对象转为vo对象......

        return vo;
    }

    @Override
    public SysMenuEntity findEntity(Integer entityId) {
        SysMenuEntity entity = super.getById(entityId);
        if (ObjectUtil.isEmpty(entity)) {
            throw new CommonServiceException("实体未找到（{}）", entityId);
        }
        return entity;
    }

//    @Override
//    public Page<SysMenuVo> page(SysMenuPageParam pageParam) {
//        QueryWrapper<SysMenuEntity> queryWrapper = new QueryWrapper<>();
//        if (ObjectUtil.isAllNotEmpty(pageParam.getSortField(), pageParam.getSortType())) {
//            queryWrapper.orderBy(true,
//                    pageParam.getSortType() == CommonSortTypeEnum.ASC,
//                    StrUtil.toUnderlineCase(pageParam.getSortField()));
//        } else {
//            queryWrapper.lambda().orderByAsc(SysMenuEntity::getSortCode);
//        }
//        queryWrapper.lambda().orderByAsc(SysMenuEntity::getId);
//
//        Page<SysMenuEntity> entityPage = super.page(pageParam.toBaomidouPage(), queryWrapper);
//        // 此处进行远程调用或关联查询......
//
//        Page<SysMenuVo> voPage = CommonUtil.convertPage(entityPage, entity -> {
//            SysMenuVo vo = BeanUtil.toBean(entity, SysMenuVo.class);
//            // 此处进行数据翻译操作，根据不同的业务逻辑将entity对象转为vo对象......
//
//            return vo;
//        });
//        return voPage;
//    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Tree<Integer>> forest() {
        // 获取指定用户关联的菜单id集合，以及其所属角色关联的菜单id集合
        List<Integer> menuIdList = sysRelationshipService.findAllMenuIdListByUserId(StpUtil.getLoginIdAsInt());

        // 根据id集合查询菜单表
        List<SysMenuEntity> entityList =
                super.list(new LambdaQueryWrapper<SysMenuEntity>().in(SysMenuEntity::getId, menuIdList));

        // 将菜单表转换为森林结构
        List<Tree<Integer>> forest = CommonUtil.toTree(entityList, 0);
        return forest;
    }

    @Override
    public List<Tree<Integer>> forestAll() {
        return CommonUtil.toTree(super.list(), 0);
    }

}